#include "Components/ComponentPlayer.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentMiner.h"
#include "Components/ComponentAttacker.h"
#include "Components/ComponentLevel.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentSickness.h"
#include "Components/ComponentFlu.h"
#include "Components/ComponentCreatureModel.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentSleep.h"
#include "Components/ComponentClothing.h"
#include "Components/ComponentCreativeInventory.h"
#include "Components/ComponentInventory.h"
#include "Components/ComponentCraftingTable.h"
#include "Components/ComponentAntiCheat.h"

#include "Subsystems/SubsystemPickables.h"
#include "Log.h"
#include "Player/PlayerData.h"
#include "Network/Packages/ComponentPlayerPackage.h"
#include "Network/Packages/ComponentInventoryPackage.h"

#include "Blocks/Block.hpp"

#include "Expand/EventManager.h"
#include "GameSingleton.h"
#include "GameThreadPool.hpp"
#include "GameRegistry.hpp"


namespace PocketSurvival
{
    void ComponentPlayer::doDrop()
    {
        if (GameSingleton::eventManager->inventoryEvent->playerDrop(this) == false)
        {
            return;
        }

        IInventory *inventory = componentMiner->inventory;
        int32_t slotValue = inventory->getSlotValue(inventory->getActiveSlotIndex());
        int32_t num3 = inventory->removeSlotItems(inventory->getActiveSlotIndex(), inventory->getSlotCount(inventory->getActiveSlotIndex()));
        std::shared_ptr<Vector3> value2 = std::make_shared<Vector3>(8.0f * componentMiner->componentCreature->componentCreatureModel->getEyeRotation().toMatrix().getForward());
        if (slotValue != 0 && num3 != 0)
        {
            Vector3 position;
            componentBody->getPosition(position);

            position += Vector3(0.0f, componentBody->boxSize.posY * 0.9f, 0.0f) + 0.25f * componentBody->getMatrix().getForward();
            GameSingleton::singletonPtr->getSingleton<SubsystemPickables>()->addPickable(slotValue, num3, position, value2, nullptr);
        }
    }

    void ComponentPlayer::dragDrop(const Vector3 &dropVelocity, IInventory *inventory, uint32_t slotIndex, uint32_t count)
    {
        if (GameSingleton::eventManager->inventoryEvent->playerDrapDrop(this, dropVelocity, inventory, slotIndex, count) == false)
        {
            return;
        }

        int32_t slotValue = inventory->getSlotValue(slotIndex);
        uint32_t num = inventory->removeSlotItems(slotIndex, count);
        if (num > 0)
        {
            Vector3 startPosition;
            componentBody->getPosition(startPosition);
            
            startPosition.posY += componentBody->boxSize.posY * 0.9f;
            GameSingleton::singletonPtr->getSingleton<SubsystemPickables>()->addPickable(slotValue, num, startPosition, std::make_shared<Vector3>(dropVelocity), nullptr);
        }
    }

    void ComponentPlayer::update(float dt)
    {
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        if (playerData == nullptr)
        {
            throw std::runtime_error("ComponentPlayer : PlayerData不能为空");
        }
        if (timeOfDay->periodicGameTimeEvent(0.1f, 0.0f)) // && playerData->client != nullptr)
        {
            std::shared_ptr<IPackage> package = std::make_shared<ComponentPlayerPackage>(this, PlayerPackageType::BodyUpdate);
            package->sendType = PackageSendType::EXCEPT;
            package->client = playerData->client;
            // package->sendMethod = DeliveryMethod::ReliableSequenced;
            GameSingleton::packageManager->addSendPackageToQueue(package);
        }

        // dig
        if (digEvents.size() > 0)
        {
            digEventItem = digEvents.front();
            digEvents.pop();
            // spdlog::info("dig evnet: {}", (uint8_t)digEventItem.digEvent);
        }

        if (componentHealth->health > 0)
        {
            switch (digEventItem.digEvent)
            {
            case PlayerDigEvent::Start:
                if (digEventItem.digRay != nullptr && digEventItem.digRaycast != nullptr)
                {
                    componentMiner->dig(*(digEventItem.digRaycast));
                }
                break;
            case PlayerDigEvent::Cancel:
                componentMiner->digCellFace = nullptr;
                digEventItem.digEvent = PlayerDigEvent::None;
                componentMiner->digProgress = 0.0f;
                break;
            case PlayerDigEvent::End:
                if (digEventItem.digRay != nullptr && digEventItem.digRaycast != nullptr)
                {
                    // spdlog::info("dig end");
                    componentMiner->dig(*(digEventItem.digRaycast), true);
                }
                digEventItem.digEvent = PlayerDigEvent::None;
                break;
            }
            // aim
            if (aimEvents.size() > 0)
            {
                aimEventItem = aimEvents.front();
                aimEvents.pop();
            }
            switch (aimEventItem.aimEvent)
            {
            case PlayerAimEvent::InProgress:
                componentMiner->aim(*(aimEventItem.aim), AimState::InProgress);
                break;
            case PlayerAimEvent::Cancel:
                componentMiner->aim(*(aimEventItem.aim), AimState::Cancelled);
                aimEventItem.aimEvent = PlayerAimEvent::None;
                break;
            case PlayerAimEvent::Complete:
                componentMiner->aim(*(aimEventItem.aim), AimState::Completed);
                aimEventItem.aimEvent = PlayerAimEvent::None;
                break;
            }
            // interact
            if (interactEvents.size() > 0)
            {
                InteractEventItem &interactEventItem = interactEvents.front();
                switch (interactEventItem.interactEvent)
                {
                case PlayerInteractEvent::Start:
                    if (componentMiner->use(interactEventItem.interactRay) == false)
                    {
                        if (interactEventItem.placeRaycast != nullptr)
                        {
                            componentMiner->interact(*(interactEventItem.placeRaycast));
                        }
                    }
                    break;
                case PlayerInteractEvent::Place:
                    if (interactEventItem.placeRaycast != nullptr)
                    {
                        // interactEventItem.placeRaycast->ray.Position = componentBody->position;
                        componentMiner->place(*(interactEventItem.placeRaycast));
                    }
                    break;
                }
                interactEvents.pop();
            }
            // hit
            if (hitEvents.getStackSize() > 0)
            {
                if (hitEvents.getStackSize() > 5)
                {
                    Log::Warn(fmt::format("玩家 {} 在一个Tick的时间内打出了 {} 段伤害，疑似开挂", playerData->username, hitEvents.getStackSize()));
                }
                HitEventItem &hitItem = hitEvents.top();
                entt::entity entity = (entt::entity)hitItem.entityID;
                if (GameSingleton::gameRegistry->valid(entity) == true)
                {
                    componentAttacker->hit(entity, hitItem.hitPosition, hitItem.hitDirection);
                }
                hitEvents.clear();
            }
        }

        if (pickBlockRay3 != nullptr)
        {
            if (playerData->gameMode != GameMode::Creative)
            {
                Log::Warn(fmt::format("玩家[{}]{}，不是创造模式，但是却使用了创造模式的鼠标中键功能", playerData->username, playerData->playerGuid.toString()));
                return;
            }
            RaycastData raycastData = componentMiner->raycast(*pickBlockRay3, RaycastMode::Digging);
            pickBlockRay3 = nullptr;
            if (raycastData.terrainRaycastResult != nullptr)
            {
                int32_t value3 = raycastData.terrainRaycastResult->value;
                value3 = Terrain::ReplaceLight(value3, 0);
                int32_t num4 = Terrain::ExtractContents(value3);
                Block *block2 = GameSingleton::blocksManager->blocks[num4];
                int32_t num5 = 0;

                std::vector<int32_t> creativeValues;
                block2->getCreativeValues(creativeValues);

                for (int32_t itemValue : creativeValues)
                {
                    if (itemValue == value3)
                    {
                        num5 = value3;
                        break;
                    }
                }
                if (num5 == 0 && block2->IsNonDuplicable == false)
                {
                    std::vector<BlockDropValue> dropValueVec;
                    block2->getDropValues(value3, 0, INT32_MAX, dropValueVec);
                    if (dropValueVec.size() > 0 && dropValueVec[0].count > 0)
                    {
                        num5 = dropValueVec[0].value;
                    }
                }
                if (creativeValues.size() == 0)
                {
                    return;
                }
                int32_t num6 = -1;
                for (int32_t i = 0; i < 10; i++)
                {
                    if (componentCreativeInventory->getSlotCapacity(i, num5) > 0 && 
                        componentCreativeInventory->getSlotCount(i) > 0 &&
                        componentCreativeInventory->getSlotValue(i) == num5)
                    {
                        num6 = i;
                        break;
                    }
                }
                if (num6 < 0)
                {
                    for (int32_t j = 0; j < 10; j++)
                    {
                        if (componentCreativeInventory->getSlotCapacity(j, num5) > 0 &&
                            (componentCreativeInventory->getSlotCount(j) == 0 || componentCreativeInventory->getSlotValue(j) == 0))
                        {
                            num6 = j;
                            break;
                        }
                    }
                }
                if (num6 < 0)
                {
                    num6 = componentCreativeInventory->getActiveSlotIndex();
                }
                componentCreativeInventory->removeSlotItems(num6, INT32_MAX);
                componentCreativeInventory->addSlotItems(num6, num5, 1);
                componentCreativeInventory->setActiveSlotIndex(num6);

                std::shared_ptr<IPackage> package = std::make_shared<ComponentInventoryPackage>(componentCreativeInventory, ComponentInventoryPackageType::ActiveSlotChange);
                package->sendType = PackageSendType::EXCEPT;
                package->client = playerData->client;
                package->sendMethod = DeliveryMethod::ReliableOrdered;
                GameSingleton::packageManager->addSendPackageToQueue(package);
            }
        }
    }

    void ComponentPlayer::setNetPosition(const Vector3 &position)
    {
        componentAntiCheat->lastPosition = position;
        componentBody->setPosition(position);

        std::shared_ptr<ComponentPlayerPackage> package = std::make_shared<ComponentPlayerPackage>(this, PlayerPackageType::PositionSet);
        package->sendType = PackageSendType::TARGET;
        package->client = playerData->client;
        GameSingleton::packageManager->addSendPackageToQueue(package);
    }

    static const std::string componentName = "Player";
    const std::string &ComponentPlayer::getName() const
    {
        return componentName;
    }
    const std::string &ComponentPlayer::GetName()
    {
        return componentName;
    }
    void ComponentPlayer::load(const nlohmann::json &jsonData)
    {
        // PlayerData 和 ComponetnPlayer 互相绑定
        playerData = GameSingleton::gameRegistry->get<PlayerData *>(entity)->shared_from_this();
        playerData->componentPlayer = this;

        componentBody = GameSingleton::gameRegistry->get<ComponentBody *>(entity);
        componentMiner = GameSingleton::gameRegistry->get<ComponentMiner *>(entity);
        componentAttacker = GameSingleton::gameRegistry->get<ComponentAttacker *>(entity);
        componentLevel = GameSingleton::gameRegistry->get<ComponentLevel *>(entity);
        componentHealth = GameSingleton::gameRegistry->get<ComponentHealth *>(entity);
        componentSickness = GameSingleton::gameRegistry->get<ComponentSickness *>(entity);
        componentFlu = GameSingleton::gameRegistry->get<ComponentFlu *>(entity);
        componentVitalStats = GameSingleton::gameRegistry->get<ComponentVitalStats *>(entity);
        componentSleep = GameSingleton::gameRegistry->get<ComponentSleep *>(entity);
        componentClothing = GameSingleton::gameRegistry->get<ComponentClothing *>(entity);
        componentCreativeInventory = GameSingleton::gameRegistry->get<ComponentCreativeInventory *>(entity);
        componentInventory = GameSingleton::gameRegistry->get<ComponentInventory *>(entity);
        componentCraftingTable = GameSingleton::gameRegistry->get<ComponentCraftingTable *>(entity);

        componentAntiCheat = GameSingleton::gameRegistry->get<ComponentAntiCheat *>(entity);
    }
    void ComponentPlayer::save(nlohmann::json &jsonData)
    {
        jsonData["FireDuration"] = jsonData;
    }
    void ComponentPlayer::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "ConstantSpawn", "bool", false);
        MSG_VALUE_SET(jsonData, "PlayerIndex", "int", playerData->playerDataID);
        MSG_VALUE_SET(jsonData, "PlayerGuid", "System.Guid", playerData->playerGuid.toString());
    }
} // namespace PocketSurvival